#include "i2c_hal.h"

//-- Defines ------------------------------------------------------------------
// I2C IO-Pins  /* -- adapt the defines for your uC -- */

/* 端口声明 */
// SDA on port A, bit 12
#define SDA_LOW()   (GPIOA->BSRR = 0X10000000)// set SDA to low 
#define SDA_OPEN()  (GPIOA->BSRR = 0X00001000)// set SDA to open-drain 
#define SDA_READ    (GPIOA->IDR & 0X1000)// read SDA
#define SDA_IN      {GPIOA->CRH&=0XFFF0FFFF;GPIOA->CRH|=(u32)8<<16;}
#define SDA_OUT     {GPIOA->CRH&=0XFFF0FFFF;GPIOA->CRH|=(u32)3<<16;}
// SCL on port A, bit 11    /* -- adapt the defines for your uC -- */ 
#define SCL_LOW()   (GPIOA->BSRR = 0X08000000)// set SCL to low
#define SCL_OPEN()  (GPIOA->BSRR = 0X00000800)// set SCL to open-drain 
#define SCL_READ    (GPIOA->IDR & 0X0800)// read SCL
#define SCL_IN      {GPIOA->CRH&=0XFFFF0FFF;GPIOA->CRH|=(u32)8<<12;}
#define SCL_OUT     {GPIOA->CRH&=0XFFFF0FFF;GPIOA->CRH|=(u32)3<<12;}

//-- Static function prototypes ----------------------------------------------- 
static etError I2c_WaitWhileClockStreching(u8t timeout);

//-----------------------------------------------------------------------------
void I2c_Init(void) /* -- adapt the init for your uC -- */
{
    GPIO_InitTypeDef  GPIO_InitStructure;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//使能GPIOA时钟

    //GPIOA11,A12初始化设置
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11 | GPIO_Pin_12;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;//开漏输出模式
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//50MHz
    GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化
    GPIO_SetBits(GPIOA,GPIO_Pin_11| GPIO_Pin_12);//GPIOA11,A12设置高
}

//-----------------------------------------------------------------------------
void I2c_StartCondition(void)
{
    SDA_OPEN();
    DelayMicroSeconds(1); 
    SCL_OPEN();
    DelayMicroSeconds(1); 
    SDA_LOW();
    DelayMicroSeconds(10); // hold time start condition (t_HD;STA) 
    SCL_LOW();
    DelayMicroSeconds(10);
}

//-----------------------------------------------------------------------------
void I2c_StopCondition(void)
{
    SCL_LOW();
    DelayMicroSeconds(1); 
    SDA_LOW();
    DelayMicroSeconds(1); 
    SCL_OPEN();
    DelayMicroSeconds(10); // set-up time stop condition (t_SU;STO) 
    SDA_OPEN();
    DelayMicroSeconds(10);
}

//-----------------------------------------------------------------------------
etError I2c_WriteByte(u8t txByte)
{
    etError error = NO_ERROR; 
    u8t mask;
    for(mask = 0x80; mask > 0; mask >>= 1)// shift bit for masking (8 times)
    {
        if((mask & txByte) == 0) SDA_LOW(); // masking txByte, write bit to SDA-Line
        else    SDA_OPEN();
        DelayMicroSeconds(1);   // data set-up time (t_SU;DAT)
        SCL_OPEN(); // generate clock pulse on SCL
        DelayMicroSeconds(5);   // SCL high time (t_HIGH) 
        SCL_LOW();
        DelayMicroSeconds(1);   // data hold time(t_HD;DAT)
    }
    SDA_OPEN(); // release SDA-line
    SCL_OPEN(); // clk #9 for ack
    DelayMicroSeconds(5);   // data set-up time (t_SU;DAT) 
    if(SDA_READ) error = ACK_ERROR; // check ack from i2c slave 
    SCL_LOW();
    DelayMicroSeconds(20);  // wait to see byte package on scope
    return error;   // return error code
}

//-----------------------------------------------------------------------------
etError I2c_ReadByte(u8t *rxByte, etI2cAck ack, u8t timeout)
{
    etError error = NO_ERROR; 
    u8t mask;
    *rxByte = 0x00;
    SDA_OPEN(); // release SDA-line
    for(mask = 0x80; mask > 0; mask >>= 1) // shift bit for masking (8 times)
    {
        SCL_OPEN(); // start clock on SCL-line
        DelayMicroSeconds(1);   // clock set-up time (t_SU;CLK)  
        error = I2c_WaitWhileClockStreching(timeout);// wait while clock streching
        DelayMicroSeconds(3); //    SCL high time (t_HIGH) 
        if(SDA_READ) *rxByte |= mask;   //read bit
        SCL_LOW();  
        DelayMicroSeconds(1);   //  data hold time(t_HD;DAT)
    }   
    if(ack == ACK) SDA_LOW();   //  send acknowledge if necessary
    else    SDA_OPEN(); 
    DelayMicroSeconds(1);   //  data set-up time (t_SU;DAT)
    SCL_OPEN(); //  clk #9 for ack
    DelayMicroSeconds(5);   //  SCL high time (t_HIGH)
    SCL_LOW();  
    SDA_OPEN(); //  release SDA-line
    DelayMicroSeconds(20);//    wait to see byte package on scope

    return error;   //return with no error
}

//-----------------------------------------------------------------------------
etError I2c_GeneralCallReset(void)
{
    etError error;

    I2c_StartCondition();
    error = I2c_WriteByte(0x00);
    if(error == NO_ERROR) error = I2c_WriteByte(0x06);

    return error;
}

//-----------------------------------------------------------------------------
static etError I2c_WaitWhileClockStreching(u8t timeout)
{
    etError error = NO_ERROR;

    while(SCL_READ == 0)
    {
        if(timeout-- == 0) return TIMEOUT_ERROR; 
        DelayMicroSeconds(1000);
    }
    
    return error;
}
